﻿"use strict";
function xmaslite() {
	console.log("xmaslite init");
	//var me=this;
	var balls = [];
	var selected = null;
	var d3mJS = new D3mJS();
	d3mJS.init();
	/*
	console.log("c "+d3mJS._tickers.length);
	new Ticker(d3mJS, 20, 1, 5, function (n) {
	console.log("n "+n);
	}, function () {
	//console.log("done "+d3mJS._tickers.length);
	}).go();

	console.log("break");return;
	 */
	var treeRadius = 60;
	var tree = new ItemConeField(treeRadius, 130);
	tree.addTo(d3mJS);
	var treeY = -60;
	tree.move(0, treeY, 0);
	var cymaterial = new THREE.MeshPhongMaterial({
			color : 0x993300,
			shininess : 30 * 30.1,
			specular : 0xffffff,
			shading : THREE.FlatShading,
			//map : THREE.ImageUtils.loadTexture('images/xsshop.jpg'),
			side : THREE.DoubleSide
		});
	var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(1, 9, 360, 10, 10, false), cymaterial);
	//cylinder.overdraw = true;
	cylinder.position.y = -120;
	d3mJS.scene.add(cylinder);

	var radius = 190;

	function angle(x, z) {
		var r = Math.sqrt(x * x + z * z);
		var asin = Math.asin(z / r);
		var acos = Math.acos(x / r);
		var a = acos;
		if (asin < 0) {
			a = -acos;
		}
		return a;
	}
	function startRotateLeftRightTo(ball) {
		if (ball.sphere.position.x == 0 && ball.sphere.position.z == 0) {
			return;
		}
		//var br = Math.sqrt(ball.sphere.position.x * ball.sphere.position.x + ball.sphere.position.z * ball.sphere.position.z);
		//console.log(br);
		var ba = angle(ball.sphere.position.x, ball.sphere.position.z);
		var ca = angle(d3mJS.camera.position.x, d3mJS.camera.position.z);

		var delta = ba - ca;
		//console.log(ba / Math.PI+ " - " + ca / Math.PI + " = " + delta / Math.PI+" / "+ba + " - " + ca  + " = " + delta );
		if (delta > Math.PI) {
			ca = ca + 2 * Math.PI;
		}
		if (delta < -Math.PI) {
			ca = ca - 2 * Math.PI;
		}
		new Ticker(d3mJS, 20, ca, ba, function (n) {
			//d3mJS.camera.position.y = n;
			d3mJS.camera.position.x = Math.cos(n) * radius;
			d3mJS.camera.position.z = Math.sin(n) * radius;
			//d3mJS.camera.lookAt(new THREE.Vector3(0, d3mJS.camera.position.y * 0.7 - 80, 0));
		}, function () {
			d3mJS.camera.position.x = Math.cos(ba) * radius;
			d3mJS.camera.position.z = Math.sin(ba) * radius;
			//d3mJS.camera.lookAt(new THREE.Vector3(0, d3mJS.camera.position.y * 0.7 - 80, 0));
		}).go();
	}
	function startMoveTo(ball) {
		//d3mJS.camera.position.y = ball.sphere.position.y;
		var by = ball.sphere.position.y + 40;
		var cy = d3mJS.camera.position.y;
		new Ticker(d3mJS, 20, cy, by, function (n) {
			d3mJS.camera.position.y = n;
			adjustCameraY();
		}, function () {
			d3mJS.camera.position.y = by;
			adjustCameraY();
		}).go();
	}
	function adjustCameraY() {
		if (d3mJS.camera.position.y < 70) {
			d3mJS.camera.position.y = 70;
		}
		if (d3mJS.camera.position.y > 160) {
			d3mJS.camera.position.y = 160;
		}
		d3mJS.camera.lookAt(new THREE.Vector3(0, d3mJS.camera.position.y * 0.7 - 90, 0));
		//console.log(d3mJS.camera.position);
	}
	function findBall(level, cell) {
		for (var i = 0; i < balls.length; i++) {
			if (balls[i].anchor.level == level && balls[i].anchor.cell == cell) {
				return balls[i];
			}
		}
		//console.log("can't find " + level + " / " + cell);
		return null;
	}
	function findBallByVertice(vertice) {
		for (var i = 0; i < balls.length; i++) {
			if (balls[i].anchor.level == vertice.level && balls[i].anchor.cell == vertice.cell) {
				return balls[i];
			}
		}
		return null;
	}
	function findVertice(_level, _cell) {
		//console.log("findVertice ",_level,_cell,tree.vertices.length);
		for (var i = 0; i < tree.vertices.length; i++) {
			var vrt = tree.vertices[i];
			//console.log("test ",vrt.level,vrt.cell);
			if (vrt.level == _level && vrt.cell == _cell) {
				//console.log(vrt);
				//console.log("found ",vrt,vrt.level,vrt.cell,_level,_cell);
				return vrt.clone();
			}
		}
		//console.log("can't find " + level + " / " + cell);
		return null;
	}

	function findDownEmptyLevelCell(level, cell) {
		//console.log("findDownEmptyLevelCell "+level+"/"+cell);
		var v = null;
		var b = null;
		if (level > 0) {
			if (level < tree.levels) {
				var side = Math.floor(cell / level);
				var step = cell - side * level;
				if (step == 0) {
					var downDown = step + side * (level + 1) - 1;
					if (downDown < 0) {
						downDown = 3 * (level + 1) - 1;
					}
					v = new THREE.Vector2(level + 1, downDown);
					b = findBall(v.x, v.y);
					if (b == null) {
						return v;
					}
				}
				var leftDown = step + side * (level + 1);
				if (leftDown < 0) {
					leftDown = 3 * (level + 1) - 1;
				}
				v = new THREE.Vector2(level + 1, leftDown);
				b = findBall(v.x, v.y);
				if (b == null) {
					return v;
				}
				
				
				
				var rightDown = step + side * (level + 1) + 1;
				if (rightDown >= 3 * (level + 1)) {
					rightDown = 0;
				}
				v = new THREE.Vector2(level + 1, rightDown);
				b = findBall(v.x, v.y);
				if (b == null) {
					return v;
				}

				
/*
				if (step == 0) {
					var leftDown = step + side * (level + 1) - 1;
					if (leftDown < 0) {
						leftDown = 3 * (level + 1) - 1;
					}
					v = new THREE.Vector2(level + 1, leftDown);
					b = findBall(v.x, v.y);
					//if (b == null) {
						return v;
					//}
				}*/
			}
		} else {
			v = new THREE.Vector2(1, 0);
			b = findBall(v.x, v.y);
			if (b == null) {
				return v;
			} else {
				v = new THREE.Vector2(1, 1);
				b = findBall(v.x, v.y);
				if (b == null) {
					return v;
				} else {
					v = new THREE.Vector2(1, 2);
					b = findBall(v.x, v.y);
					if (b == null) {
						return v;
					}
				}
			}
		}
		//console.log("findDownEmptyLevelCell none");
		return null;
	}
	function findForConnect(level, cell) {
		var near = [];
		if (level > 0) {
			var side = Math.floor(cell / level);
			var step = cell - side * level;
			var rightCell = cell + 1;
			if (rightCell > level * 3 - 1) {
				rightCell = 0;
			}
			near.push(findBall(level, rightCell));
			var leftCell = cell - 1;
			if (leftCell < 0) {
				leftCell = level * 3 - 1;
			}
			near.push(findBall(level, leftCell));
		}
		return near;
	}
	function findNearest(level, cell) {
		var near = [];
		if (level > 0) {
			var side = Math.floor(cell / level);
			var step = cell - side * level;
			var rightCell = cell + 1;
			if (rightCell > level * 3 - 1) {
				rightCell = 0;
			}
			near.push(findBall(level, rightCell));
			var leftCell = cell - 1;
			if (leftCell < 0) {
				leftCell = level * 3 - 1;
			}
			near.push(findBall(level, leftCell));
			if (level == 1) {
				near.push(findBall(0, 0));
			} else {
				if (level > 1) {
					var rightUpper = step + side * (level - 1);
					if (rightUpper >= 3 * (level - 1)) {
						rightUpper = 0;
					}
					near.push(findBall(level - 1, rightUpper));
					if (step > 0) {
						var leftUpper = step + side * (level - 1) - 1;
						near.push(findBall(level - 1, leftUpper));
					}
				}
			}
			if (level < tree.levels) {
				var rightDown = step + side * (level + 1) + 1;
				if (rightDown >= 3 * (level + 1)) {
					rightDown = 0;
				}
				near.push(findBall(level + 1, rightDown));
				var leftDown = step + side * (level + 1);
				if (leftDown < 0) {
					leftDown = 3 * (level + 1) - 1;
				}
				near.push(findBall(level + 1, leftDown));
				if (step == 0) {
					var leftDown = step + side * (level + 1) - 1;
					if (leftDown < 0) {
						leftDown = 3 * (level + 1) - 1;
					}
					near.push(findBall(level + 1, leftDown));
				}
			}
		} else {
			near.push(findBall(1, 0));
			near.push(findBall(1, 1));
			near.push(findBall(1, 2));
		}
		return near;
	}
	function isNear(b1, b2) {
		var near = findNearest(b1.anchor.level, b1.anchor.cell);
		for (var i = 0; i < near.length; i++) {
			if (near[i].anchor.level == b2.anchor.level && near[i].anchor.cell == b2.anchor.cell) {
				return true;
			}
		}
		return false;
	}
	function startSwap(b1, b2) {
		console.log("swap " + b1.anchor.level + "/" + b1.anchor.cell + " and " + b2.anchor.level + "/" + b2.anchor.cell);
		var level1 = b1.anchor.level;
		var cell1 = b1.anchor.cell;
		b1.anchor.level = b2.anchor.level;
		b1.anchor.cell = b2.anchor.cell;
		b2.anchor.level = level1;
		b2.anchor.cell = cell1;
		var x1 = b1.sphere.position.x;
		var y1 = b1.sphere.position.y;
		var z1 = b1.sphere.position.z;
		var x2 = b2.sphere.position.x;
		var y2 = b2.sphere.position.y;
		var z2 = b2.sphere.position.z;
		var st = 20;
		new Ticker(d3mJS, 10, 1, st, function (n) {
			b1.move(x1 + n * (x2 - x1) / st, y1 + n * (y2 - y1) / st, z1 + n * (z2 - z1) / st);
			b2.move(x2 + n * (x1 - x2) / st, y2 + n * (y1 - y2) / st, z2 + n * (z1 - z2) / st);
		}, function () {
			b1.move(x2, y2, z2);
			b2.move(x1, y1, z1);
			connectBalls();
		}).go();
	}

	function unlightAll() {
		//console.log(balls.length);
		for (var i = 0; i < balls.length; i++) {
			//console.log(i);
			balls[i].unlight();
		}
	}
	function tap(level, cell) {
		//console.log("tap " + level+"/"+cell);


		var ball = findBall(level, cell);

		//isNear(ball, null);

		if (ball.on) {
			selected = null;
			ball.unlight();
		} else {
			if (selected == null) {
				//unlightAll();
				selected = ball;
				ball.light();
				//var near=findDownEmptyLevelCell(ball.anchor.level,ball.anchor.cell);
				//console.log(near);
				//if(near!=null){
				//	var b=findBall(near.x,near.y);
				//	if(b!=null){
				//	b.light();}
				//	}

				
			} else {
				selected.unlight();
				if (isNear(ball, selected)) {
					startSwap(ball, selected);
					selected = null;
				} else {
					selected = ball;
					ball.light();
				}

			}

		}

		//console.log(ball.anchor);
		startRotateLeftRightTo(ball);
		startMoveTo(ball);
		//d3mJS.camera.lookAt(new THREE.Vector3(0, d3mJS.camera.position.y * 0.7 - 80, 0));
		//d3mJS.camera.updateProjectionMatrix();
		/*
		//console.log(ba + " / " + ca);
		/*
		var cy=d3mJS.camera.position.y;
		var ty=ball.sphere.position.y;
		new Ticker(d3mJS, 10,cy, ty, function (n) {
		d3mJS.camera.position.y=n;
		}, function () {
		d3mJS.camera.position.y=ty;
		}).go();
		 */
	}
	var lasers = [];
	function addLaserRay(fromBall, toBall) {
		var laser = new ItemLaserRay(3, fromBall.color);
		laser.attachTo(tree.mesh, d3mJS);
		laser.align(//
			new THREE.Vector3(fromBall.sphere.position.x, fromBall.sphere.position.y, fromBall.sphere.position.z) //
		, new THREE.Vector3(toBall.sphere.position.x, toBall.sphere.position.y, toBall.sphere.position.z) //
		);
		lasers.push(laser);
	}
	function startMoveDown(ball, level, cell) {
		if (ball.locked) {
			return false;
		}
		ball.locked = true;
		//console.log("startMove " + ball.anchor.level + "/" + ball.anchor.cell + " to " + level + "x" + cell);
		var vertice = findVertice(level, cell);
		//console.log("vertice",vertice,vertice.level,vertice.cell);


		ball.anchor.level = level;
		ball.anchor.cell = cell;

		var x1 = ball.sphere.position.x;
		var y1 = ball.sphere.position.y;
		var z1 = ball.sphere.position.z;

		if (vertice == null) {
			console.log("not found " + level + "/" + cell);
		}

		var p = vertice2ball(vertice.x, vertice.y, vertice.z);
		//console.log("move ",ball.sphere.position," to ",p);

		var x2 = p.x; //b2.sphere.position.x;
		var y2 = p.y; //b2.sphere.position.y;
		var z2 = p.z; //b2.sphere.position.z;
		//console.log("place " + Math.round(x1) + " x " + Math.round(y1) + " x " + Math.round(z1) + " to " + Math.round(x2) + " x " + Math.round(y2) + " x " + Math.round(z2));
		var st = 10;
		new Ticker(d3mJS, 5, 1, st, function (n) {
			ball.move(x1 + n * (x2 - x1) / st, y1 + n * (y2 - y1) / st, z1 + n * (z2 - z1) / st);
		}, function () {
			ball.move(x2, y2, z2);
			ball.locked = false;
		}).go();
		return true;
	}
	function removeLasers() {
		console.log("removeLasers");
		for (var i = 0; i < lasers.length; i++) {
			//console.log(i);
			lasers[i].detach();
			//console.log(d3mJS.tickers.length);
		}
		//console.log(lasers.length);
		//unlightAll();

	}
	function removeFirstLightedBall() {
		for (var i = 0; i < balls.length; i++) {
			var b = balls[i];
			if (b.on) {
				b.detach();
				balls.splice(i, 1);
				//console.log("removeFirstLightedBall "+b.anchor.level+"/"+b.anchor.cell);
				return true;
			}
		}
	}
	function removeRoundBalls() {
		console.log("removeRoundBalls");
		while (removeFirstLightedBall()) {
			//
		}
		/*
		for (var i = 0; i < balls.length; i++) {
		var b = balls[i];
		if (b.on) {
		b.detach();
		//balls.splice(i,1);
		}
		}*/
	}
	function addNextBall() {
		for (var i = 0; i < balls.length; i++) {
			var b = balls[i];

			if (b.anchor.level == 0 && b.anchor.cell == 0) {
				//console.log("skip next ball", b,tree.vertices[0]);
				return;
			}
		}
		addRandomBall(tree.vertices[0]);
	}
	function moveDown2() {
		new Ticker(d3mJS, 10, 1, 10, function (n) {
			//
		}, function () {
			for (var i = 0; i < tree.vertices.length; i++) {
				var vertice = tree.vertices[i];
				var ball = findBallByVertice(vertice);
				if (ball == null) {
					//console.log("add",vertice);
					addRandomBall(vertice);
				}
			}
		}).go();

	}
	function moveDown() {
		console.log("moveDown");
		var delta = 0;
		var flag = false;
		for (var i = 0; i < tree.vertices.length; i++) {
			var v = tree.vertices[i];
			var ball = findBall(v.level, v.cell);
			if (ball != null) {
				//console.log(v.level + "/" + v.cell + ": " + findEmptyCell(v.level, v.cell));
				//console.log(v);
				var levelCell = findDownEmptyLevelCell(v.level, v.cell);
				if (levelCell != null) {
					if (startMoveDown(ball, levelCell.x, levelCell.y)) {
						flag = true;
					}
				}
			}
		}

		if (flag) {
			new Ticker(d3mJS, 5, 1, 10, function (n) {
				//
			}, function () {
				addNextBall();
				moveDown();
			}).go();
		} else {
			new Ticker(d3mJS, 5, 1, 10, function (n) {
				//
			}, function () {
				addNextBall();
				//moveDown();
				connectBalls();
			}).go();
		}
	}

	function connectNear(ball) {
		//console.log(ball.anchor.level + " / " + ball.anchor.cell);
		var exists = false;
		var near = findForConnect(ball.anchor.level, ball.anchor.cell);
		for (var i = 0; i < near.length; i++) {
			if (near[i] == null) {
				//console.log("break",ball);
				ball.light();
				return false;
			}
			if (!near[i].on) {
				if (ball.color == near[i].color) {
					near[i].light();
					ball.light();
					addLaserRay(ball, near[i]);
					/*var laser = new ItemLaserRay(3, ball.color);
					laser.attachTo(tree.mesh, d3mJS);
					//laser.align(new THREE.Vector3(0, 133+treeY, 0), new THREE.Vector3(99, 99, 99));
					laser.align(//
					new THREE.Vector3(ball.sphere.position.x, ball.sphere.position.y, ball.sphere.position.z) //, new THREE.Vector3(near[i].sphere.position.x, near[i].sphere.position.y, near[i].sphere.position.z) //
					);*/
					//console.log("next " + near[i].anchor.level + " / " + near[i].anchor.cell);
					exists = true;
					connectNear(near[i]);

				}
			}
		}
		return exists;
	}
	function connectBalls() {
		console.log("connectBalls");
		var flag = false;
		for (var i = 0; i < balls.length; i++) {
			var ball = balls[i];
			if (!ball.on) {
				ball.light();
				var noLight = true;
				var near = findForConnect(ball.anchor.level, ball.anchor.cell);
				for (var k = 0; k < near.length; k++) {
					if (near[k] != null) {
						//if (!near[k].on) {
							if (ball.color == near[k].color) {
								if (connectNear(near[k])) {
									noLight = false;
									addLaserRay(ball, near[k]);
									flag = true;
								}
							}
						}
					//}
				}
				if (noLight) {
					ball.unlight();
				}
			}
		}
		if (flag) {
			new Ticker(d3mJS, 20, 0, 1, function (n) {
				//
			}, function () {
				removeLasers();
				removeRoundBalls();
				moveDown();
			}).go();
		}
	}
	function vertice2ball(x, y, z) {
		var v = new THREE.Vector3();
		if (x == 0 && z == 0) {
			v.x = x;
			v.y = y + 3;
			v.z = z;
			//b.move(v.x, v.y + 3, v.z);
		} else {
			var r = Math.sqrt(x * x + z * z);
			r = r + 3;
			var a = angle(x, z);
			v.x = Math.cos(a) * r;
			v.y = y;
			v.z = Math.sin(a) * r;
			//b.move(Math.cos(a) * r, v.y, Math.sin(a) * r);
		}
		return v;
	}
	function randomColor() {
		var c = 0x0000ff;
		var rnd = Math.random();
		if (rnd < 0.2) {
			c = 0xff0000;
		} else {
			if (rnd < 0.4) {
				c = 0x0000ff;
			} else {
				if (rnd < 0.6) {
					c = 0xff00ff;
				} else {
					if (rnd < 0.8) {
						c = 0xffff00;
					} else {
						c = 0x00ffff;
					}
				}
			}
		}
		return c;
	}
	function addRandomBall(v) {
		var b = new ItemXmsBall(randomColor());
		b.locked = false;
		b.anchor = new THREE.Vector3();
		b.anchor.x = v.x;
		b.anchor.y = v.y;
		b.anchor.z = v.z;
		b.anchor.level = v.level;
		b.anchor.cell = v.cell;
		balls.push(b);
		var p = vertice2ball(v.x, v.y, v.z);
		b.move(p.x, p.y, p.z);
		b.attachTo(tree.mesh, d3mJS);
	}
	function initialFillBalls() {
		for (var i = 0; i < tree.vertices.length; i++) {
			var v = tree.vertices[i];
			addRandomBall(v);
		}
	}
	function addKnobs() {
		for (var i = 0; i < tree.vertices.length; i++) {
			var v = tree.vertices[i];
			var k = new ItemKnob(15, 15, 15, 0xff00ff);
			k.move(v.x, v.y, v.z);
			k.attach(d3mJS, tree.mesh);
			k.hide();
			//k.nn = i;
			k.level = v.level;
			k.cell = v.cell;
			k.tap = function () {
				//console.log(k);
				tap(this.level, this.cell);
			};
		}
	}
	initialFillBalls();
	//connectBalls();
	addKnobs();

	var itemXYZ=ItemXYZ();
	itemXYZ.addTo(d3mJS);
	//var light = new THREE.AmbientLight( 0x222222 ); // soft white light
	//d3mJS.scene.add( light );

	var globe = new ItemInvertedGlobe(250, 'images/xsshop.jpg');
	globe.addTo(d3mJS);
	//globe.move(2, 9, 3);

	var light1 = new ItemDirectionalLight(0xff0000);
	light1.move(100, 15, 0);
	light1.addTo(d3mJS);

	var light2 = new ItemDirectionalLight(0x00ff00);
	light2.move(0, 10, 100);
	light2.addTo(d3mJS);

	var light3 = new ItemDirectionalLight(0x0000ff);
	light3.move(-100, 5, 0);
	light3.addTo(d3mJS);

	var light4 = new ItemDirectionalLight(0xff00ff);
	light4.move(0, 8, -100);
	light4.addTo(d3mJS);

	var dabAngle = 0.0;
	var dabY = 0.0;
	d3mJS.dab = function (x, y) {
		//console.log("dab");
		dabY = d3mJS.camera.position.y;
		/*var asin = Math.asin(d3mJS.camera.position.z / radius);
		var acos = Math.acos(d3mJS.camera.position.x / radius);
		dabAngle = acos;
		if (asin < 0) {
		dabAngle = -acos;
		}*/
		dabAngle = angle(d3mJS.camera.position.x, d3mJS.camera.position.z);
	};
	d3mJS.drag = function (x, y) {
		var deltaX = d3mJS.pointStartX - x;
		var deltaY = d3mJS.pointStartY - y;
		var deltaAngle = deltaX * 0.001;
		var newAngle = dabAngle - deltaAngle;
		d3mJS.camera.position.x = Math.cos(newAngle) * radius;
		d3mJS.camera.position.y = dabY - deltaY / 10;
		d3mJS.camera.position.z = Math.sin(newAngle) * radius;
		adjustCameraY();
		//d3mJS.camera.lookAt(new THREE.Vector3(0, d3mJS.camera.position.y * 0.7 - 80, 0));
	};
	d3mJS.camera.fov = 40;
	d3mJS.camera.position.x = 0;
	d3mJS.camera.position.y = 140;
	d3mJS.camera.position.z = radius;
	//d3mJS.camera.lookAt(new THREE.Vector3(0, d3mJS.camera.position.y * 0.7 - 80, 0));
	adjustCameraY();
	d3mJS.camera.updateProjectionMatrix();
	/*
	me.camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
	me.camera.position.x = -13;
	me.camera.position.y = 14;
	me.camera.position.z = 13;
	me.camera.lookAt(new THREE.Vector3(0, 0, 0));
	 */
	connectBalls();
}
